/*******************************************************************/
/* @ All Rights Reserved Copyright (C) 2014 broada,Co.,Ltd.        */
/*                                                                 */
/*  NAME      = frct_mng.c                                         */
/*  NOTE      = conntrack管理                                      */
/*  DATE      = 2014/10/08 by yaohl                                */
/*******************************************************************/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <time.h>
#include <sys/time.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>

#include "frcomm.h"
#include "frlog.h"
#include "frmem.h"
#include "frlist.h"
#include "frsutl.h"
#include "frtimer.h"
#include "conntrack.h"
#include "module.h"
#include "frame.h"
#include "frct_comm.h"
#include "frame_params.h"
#include "frct_hash.h"
#include "frtask_mng.h"
#include "frproto_detect.h"
#include "frct_expect.h"
#include "frmodule_mng.h"
#include "frreport_mng.h"
#include "frstat_system.h"
#include "frstat_pkg.h"
#include "frstat_conn.h"
#include "frstat_port.h"
#include "frssl_config.h"
#include "frlua_config.h"
#include "frct_detect_hash.h"

#define APM_CONNTRACK_RETRY  3

//是否允许删除ct
FR_STATIC int g_i_can_del_ct=FR_YES;

//当前使用的ct个数
unsigned long g_ul_ct_used=0L;

//queue总长度的限制
unsigned long g_ul_pcap_queue_used=0L;

//删除时的timeout周期sec
#define APM_TIMEOUT_DEL_SEC     1
//ref时的timeout周期sec
#define APM_TIMEOUT_REF_SEC     2

FR_STATIC Frlist_node *__frct_find(u_short us_proto, u_int ui_src, u_short us_sport, u_int ui_dest, u_short us_dport);
FR_STATIC void __frct_del(Frlist_node *p_st_node);
FR_STATIC void __frct_free(Conntrack *p_st_ct);

/*******************************************************************************
*  FUNC     :  连接跟踪初始化
*  ARGS     :  ui_table_size  (hash桶的array长度)
            :  ul_ct_max_cnt (最大多少个ct)
*  RTN      :  FR_SUCC:成功,FR_FAIL:失败
*  NOTE     :
*******************************************************************************/
int frct_init(unsigned int ui_table_size)
{
    int i_ret =FR_FAIL, i_ret_fun=FR_FAIL;

    g_ul_ct_used=0L;
    g_ul_pcap_queue_used=0L;

    i_ret_fun = frct_hash_init(ui_table_size);
    if(i_ret_fun == FR_FAIL)
        return i_ret;

    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  节点内存释放
*  ARGS     :  p_st_node  (Frlist_node *)
*  RTN      :
*  NOTE     :  注意需要在ct->lock下操作
*******************************************************************************/
FR_STATIC void __frct_hash_node_free(Frlist_node *p_st_node)
{
    Conntrack *p_st_ct =NULL;
    if(p_st_node == NULL)
        return;

    p_st_ct = (Conntrack *)FRLIST_GET_NODE_DATA(p_st_node);
    __frct_free(p_st_ct);

    return;
}

/*******************************************************************************
*  FUNC     :  连接跟踪销毁
*  ARGS     :
*  RTN      :
*  NOTE     :  时钟必须先停止
*******************************************************************************/
void frct_destroy(void)
{
    //如果还有没有运行的时钟,必须手工删除ct
    frct_hash_destory(__frct_hash_node_free);
    return;
}

/*******************************************************************************
*  FUNC     :  生成加入queue的数据节点
*  ARGS     :  ui_src    (原地址)
*           :  us_sport  (原端口)
*           :  ui_dest   (目的地址)
*           :  us_dport  (目的端口)
*           :  p_data           (数据)
*           :  ui_data_len      (数据长度)
*           :  st_timeval_pkg   (数据包发生时间)
*  RTN      :  Frlist_node *
*  NOTE     :
*******************************************************************************/
FR_STATIC Frlist_node *__fr_queue_node_mk(int i_direction
                                        , u_int ui_src, u_short us_sport
                                        , u_int ui_dest, u_short us_dport
                                        , Franalyz_cmd em_analyz_cmd
                                        , char *p_data, unsigned int ui_data_len
                                        , int i_l7_proto
                                        , struct timeval st_timeval_pkg)
{
    Apm_packet *p_st_packet;
    Frlist_node *p_st_node;

    p_st_packet = (Apm_packet *) frmalloc(sizeof(Apm_packet));
    if(!p_st_packet)
    {
        FRLOG_ERROR_DETAIL("frmalloc err");
        return NULL;
    }
    memset(p_st_packet, 0x00, sizeof(Apm_packet));

    p_st_packet->p_payload = (char *) frmalloc(ui_data_len+1);
    if(p_st_packet->p_payload == NULL)
    {
        frfree(p_st_packet);
        FRLOG_ERROR_DETAIL("frmalloc err");
        return NULL;
    }
    if(p_data && ui_data_len>0)
        memcpy(p_st_packet->p_payload, p_data, ui_data_len);
    *(p_st_packet->p_payload + ui_data_len) = '\0';
    p_st_packet->src_ip = ui_src;
    p_st_packet->src_port = us_sport;
    p_st_packet->dst_ip = ui_dest;
    p_st_packet->st_timestamp = st_timeval_pkg;
    p_st_packet->em_analyz_cmd = em_analyz_cmd;

    p_st_packet->i_direction = i_direction;
    p_st_packet->dst_port = us_dport;
    p_st_packet->ui_payload_len = ui_data_len;
    p_st_packet->i_l7_proto = i_l7_proto;
    p_st_packet->us_is_detected = FR_NO;

    p_st_node = frlist_node_create(p_st_packet);
    if(!p_st_node)
    {
        frfree(p_st_packet->p_payload);
        frfree(p_st_packet);
        FRLOG_ERROR_DETAIL("frlist_node_create err");
        return NULL;
    }

    return p_st_node;
}

/*******************************************************************************
*  FUNC     :  数据包加入到工作任务
*  ARGS     :  p_st_ct   (Conntrack *)
*           :  ui_src    (原地址)
*           :  us_sport  (原端口)
*           :  ui_dest   (目的地址)
*           :  us_dport  (目的端口)
*           :  em_analyz_cmd    (命令类型)
*           :  p_data           (数据)
*           :  ui_data_len      (数据长度)
*           :  i_l7_proto       (l7 协议号)
*           :  st_timeval_pkg   (数据包发生时间)
*  RTN      :  FR_SUCC:成功,FR_FAIL:失败
*  NOTE     :
*******************************************************************************/
FR_STATIC int __frcap_insert_task(Conntrack *p_st_ct
                                , u_short us_proto
                                , u_int ui_src, u_short us_sport
                                , u_int ui_dest, u_short us_dport
                                , Franalyz_cmd em_analyz_cmd
                                , char *p_data, unsigned int ui_data_len
                                , int i_l7_proto
                                , struct timeval st_timeval_pkg)
{
    int i_direction = FR_DIRECT_UNKNOWN, i_ret_fun=0;
    Frlist_node *p_st_queuen_node=NULL;

    if(p_st_ct == NULL )
        return FR_FAIL;

    //单纯的act包，不加入数据队列
    if( em_analyz_cmd == FRANALYZ_CMD_DATA && (p_data == NULL || ui_data_len ==0) )
        return FR_SUCC;

    i_direction = frcap_get_cs_direction(p_st_ct, ui_src, us_sport);
    if( i_direction== FR_CLIENT_IP_PORT)
        i_direction = FR_DIRECT_CLIENT2SRV;
    else if( i_direction== FR_SRV_IP_PORT)
        i_direction = FR_DIRECT_SRV2CLIENT;
    else
        i_direction = FR_DIRECT_UNKNOWN;

    //不需要解密的数据包
    p_st_queuen_node = __fr_queue_node_mk(i_direction, ui_src, us_sport, ui_dest, us_dport
                                        , em_analyz_cmd, p_data, ui_data_len, i_l7_proto, st_timeval_pkg);
    if(p_st_queuen_node)
    {
        /* 数据入队时
         * 第1次:不需要 p_st_ct->lock(这时没有竞争关系)
         * 第n次:需要 p_st_ct->lock
         */
        frlist_push(&p_st_ct->lst_cap_queue, p_st_queuen_node);
        __sync_fetch_and_add(&g_ul_pcap_queue_used, 1);
        if(p_st_ct->flg_inTask != FR_YES)
        {
            p_st_ct->flg_inTask = FR_YES;
            __sync_fetch_and_add(&p_st_ct->refcnt,1);
            i_ret_fun = frtask_add(p_st_ct);
            if(i_ret_fun == FR_FAIL)
                FRLOG_ERROR_DETAIL("frtask_add failed");
        }
        return FR_SUCC;
    }

    return FR_FAIL;
}

/*******************************************************************************
*  FUNC     :  pcap数据节点内存释放
*  ARGS     :  p_st_node  (Frlist_node *)
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frcap_node_free(Frlist_node *node)
{
    if(node == NULL || node->data==NULL)
        return;
    Apm_packet *p_st_packet = (Apm_packet *)node->data;
    if(p_st_packet->p_payload)
        frfree(p_st_packet->p_payload);
    frfree(p_st_packet);

    return;
}

/*******************************************************************************
*  FUNC     :  设置不能删除ct
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frct_del_disable(void)
{
    frct_hash_wlock();
    g_i_can_del_ct = FR_NO;
    frct_hash_unwlock();
}

/*******************************************************************************
*  FUNC     :  设置可以删除ct
*  ARGS     :
*  RTN      :
*  NOTE     :
*******************************************************************************/
void frct_del_enable(void)
{
    frct_hash_wlock();
    g_i_can_del_ct = FR_YES;
    frct_hash_unwlock();
}

/*******************************************************************************
*  FUNC     :  检查并统计僵尸连接
*  ARGS     :  p_st_ct          (Conntrack *)
*           :  p_st_timeval     (比较用时间)
*           :  i_inactive_cnt   (不活跃连接数)
*  RTN      :  FR_SUCC:僵尸连接统计完成
*           :  FR_FAIL:僵尸连接未统计
*  NOTE     :  需要加锁( p_st_ct->lock)
*******************************************************************************/
FR_STATIC int __frct_check_dead_conn(Conntrack *p_st_ct
                                   , struct timeval *p_st_timeval
                                   , int i_inactive_cnt)
{
    L7_analyzer *p_st_l7_analyzer=NULL;

    /* 还有需要处理的数据包时，不认为是僵尸连接 */
    if(p_st_ct == NULL
       || p_st_ct->flg_report_conn_dead == FR_YES
       || p_st_ct->st_l7_analyzer.p_st_l7_module == NULL
       || frlist_size(&p_st_ct->lst_cap_queue) > 0)
        return FR_FAIL;

    if(p_st_timeval->tv_sec - p_st_ct->st_connection_lastskb_time.tv_sec < st_apm_params.ui_dead_conn_time)
        return FR_FAIL;

    p_st_l7_analyzer =  &p_st_ct->st_l7_analyzer;
    if(p_st_l7_analyzer->p_st_l7_module)
        frstat_system_module_inactive_add(p_st_l7_analyzer->p_st_l7_module->i_l7_proto, i_inactive_cnt);

    return FR_SUCC;
}

/*******************************************************************************
*  FUNC     :  时钟回调函数
*  ARGS     :  i_fd          (文件句柄)
            :  s_event       (事件号)
            :  p_arg         (入力参数数据)
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC void __frct_timer_callback(int i_fd, short s_event, void *p_arg)
{
    Timer_entry *p_st_timer_entry=NULL;
    Frlist_node *p_st_node=(Frlist_node *)p_arg;
    Conntrack *p_st_ct = (Conntrack *)FRLIST_GET_NODE_DATA(p_st_node);
    struct timeval st_timeval;
    int i_flag_end=FR_NO;

    frct_hash_wlock();
    while(g_i_can_del_ct == FR_NO)
    {
        frct_hash_unwlock();
        sleep(1);
        frct_hash_wlock();
    }

    gettimeofday (&st_timeval, NULL);
    //frdbg("__frct_timer_callback run\n");
    p_st_timer_entry = p_st_ct->p_st_timer_entry;
    //程序终止
    if(g_i_flag_run == 0)
    {
        //有引用或者禁止删除ct
        if( p_st_ct->refcnt > 0 || g_i_can_del_ct == FR_NO)
        {
            p_st_timer_entry->st_delay.tv_sec = APM_TIMEOUT_REF_SEC;
            p_st_timer_entry->st_delay.tv_usec = 0;
            frtimer_add(p_st_timer_entry->p_st_event, &p_st_timer_entry->st_delay);
            frct_hash_unwlock();
            return;
        }
    }
    else
    {
        pthread_spin_lock(&p_st_ct->lock);
        //处理僵尸连接
        if(__frct_check_dead_conn(p_st_ct, &st_timeval, 1) == FR_SUCC)
            p_st_ct->flg_report_conn_dead = FR_YES;

        //有数据包，更新过期时钟
        if( p_st_ct->flg_update == FR_YES && p_st_ct->flg_status != APM_CONNECT_CLOSED )
        {
            p_st_ct->flg_update = FR_NO;
            p_st_timer_entry->st_delay.tv_sec = APM_CFG_MIN_TIMEOUT;
            p_st_timer_entry->st_delay.tv_usec = 0;
            frtimer_add(p_st_timer_entry->p_st_event, &p_st_timer_entry->st_delay);
            pthread_spin_unlock(&p_st_ct->lock);
            frct_hash_unwlock();
            return;
        }

        //检查session timeout
        if(p_st_ct->flg_report_conn_info == FR_YES && p_st_ct->flg_status != APM_CONNECT_CLOSED
           && st_timeval.tv_sec - p_st_ct->st_connection_lastskb_time.tv_sec >= st_apm_params.ui_session_timeout)
        {
            frstat_conns_del_conn_ct(p_st_ct, FR_NO);
        }

        //有引用/禁止删除ct
        if( p_st_ct->refcnt > 0 || g_i_can_del_ct == FR_NO)
        {
            p_st_timer_entry->st_delay.tv_sec = APM_TIMEOUT_REF_SEC;
            p_st_timer_entry->st_delay.tv_usec = 0;
            frtimer_add(p_st_timer_entry->p_st_event, &p_st_timer_entry->st_delay);
            pthread_spin_unlock(&p_st_ct->lock);
            frct_hash_unwlock();
            return;
        }

        //检查是否已经超时
        i_flag_end=FR_NO;
        if( p_st_ct->ip_proto == IPPROTO_TCP)
        {
            switch(p_st_ct->flg_status)
            {
                case APM_CONNECT_SYN_SENT:
                case APM_CONNECT_SYN_RECV:
                    if(st_timeval.tv_sec - p_st_ct->st_connection_lastskb_time.tv_sec >= st_apm_params.ui_tcp_syn_timeout)
                        i_flag_end = FR_YES;
                    break;
                case APM_CONNECT_ESTABLISHED:
                    if(st_timeval.tv_sec - p_st_ct->st_connection_lastskb_time.tv_sec >= st_apm_params.ui_tcp_est_timeout)
                        i_flag_end = FR_YES;
                    break;
                case APM_CONNECT_CLOSED:
                    i_flag_end = FR_YES;
                    break;
            }
        }
        else if( p_st_ct->ip_proto == IPPROTO_UDP )
        {
            switch(p_st_ct->flg_status)
            {
                case APM_CONNECT_SYN_SENT:
                case APM_CONNECT_SYN_RECV:
                case APM_CONNECT_ESTABLISHED:
                    if(st_timeval.tv_sec - p_st_ct->st_connection_lastskb_time.tv_sec >= st_apm_params.ui_udp_est_timeout)
                        i_flag_end = FR_YES;
                    break;
                case APM_CONNECT_CLOSED:
                    i_flag_end = FR_YES;
                    break;
            }
        }

        //未超时
        if(i_flag_end == FR_NO)
        {
            p_st_timer_entry->st_delay.tv_sec = APM_TIMEOUT_REF_SEC;
            p_st_timer_entry->st_delay.tv_usec = 0;
            frtimer_add(p_st_timer_entry->p_st_event, &p_st_timer_entry->st_delay);
            pthread_spin_unlock(&p_st_ct->lock);
            frct_hash_unwlock();
            return;
        }

        //timeout时连接信息上报
        if(p_st_ct->flg_status == APM_CONNECT_CLOSED )
            frct_stat_system_update(p_st_ct, 0, 0, 1, 0 );

        //上报没有关闭的连接信息
        if(p_st_ct->flg_report_conn_info == FR_YES)
            frstat_conns_del_conn_ct(p_st_ct, FR_NO);

        pthread_spin_unlock(&p_st_ct->lock);
    }

    //没有引用,删除该ct
    //防止其他地方再次加入timer
    //frtimer_del(p_st_timer_entry->p_st_event);
    frtimer_free(p_st_timer_entry->p_st_event);
    p_st_timer_entry->p_st_event = NULL;
    
    //syn timtout event
    if( (p_st_ct->flg_status == APM_CONNECT_SYN_SENT || p_st_ct->flg_status == APM_CONNECT_SYN_RECV) )
    {
        Frreport_data *p_st_report_data=NULL;
        p_st_report_data = frreport_update_create(p_st_ct, APM_REPORT_UPDATE_CONNECT_FAILED, "syn timeout", &st_timeval);
        if(p_st_report_data)
            frreport_mng_save_report_data2(&p_st_report_data);
    }

    __frct_del(p_st_node);
    __frct_free(p_st_ct);
    frct_hash_unwlock();
    //frdbg("delete ct\n");

    return;
}

/*******************************************************************************
*  FUNC     :  L7_analyzer节点初始
*  ARGS     :  p_st_l7_module  (L7_module *,没有时NULL也可)
*  RTN      :  FR_SUCC:成功, FR_FAIL:失败
*  NOTE     :
*******************************************************************************/
FR_STATIC int __frct_l7analyzer_init(L7_analyzer *p_st_l7_analyzer)
{
    if(p_st_l7_analyzer == NULL)
        return FR_FAIL;

    p_st_l7_analyzer->p_st_l7_module = NULL;
    p_st_l7_analyzer->p_st_session = (L7_session*) frmalloc(sizeof(L7_session));
    p_st_l7_analyzer->p_st_transaction = (L7_transaction*) frmalloc(sizeof(L7_transaction));
    if(p_st_l7_analyzer->p_st_session == NULL || p_st_l7_analyzer->p_st_transaction==NULL)
    {
        FRLOG_ERROR_DETAIL("frmalloc err");
        goto FAIL;
    }

    memset(p_st_l7_analyzer->p_st_session, '\0', sizeof(L7_session));
    memset(p_st_l7_analyzer->p_st_transaction, '\0', sizeof(L7_transaction));

    return FR_SUCC;

FAIL:
    if(p_st_l7_analyzer->p_st_session)
        frfree(p_st_l7_analyzer->p_st_session);
    if(p_st_l7_analyzer->p_st_transaction)
        frfree(p_st_l7_analyzer->p_st_transaction);
    return FR_FAIL;

}

/*******************************************************************************
*  FUNC     :  L7_analyzer上设置l7_module
*  ARGS     :  p_st_l7_module  (L7_module *,没有时NULL也可)
*  RTN      :  FR_SUCC:成功, FR_FAIL:失败
*  NOTE     :  需要用frct_l7analyzer_free释放原来的内存
*******************************************************************************/
int frct_l7analyzer_set(L7_analyzer *p_st_l7_analyzer, L7_module *p_st_l7_module)
{
    if(p_st_l7_analyzer == NULL)
        return FR_FAIL;

    //初始化 session
    if(p_st_l7_analyzer->p_st_session == NULL)
    {
        p_st_l7_analyzer->p_st_session = (L7_session*) frmalloc(sizeof(L7_session));
        if(p_st_l7_analyzer->p_st_session)
            memset(p_st_l7_analyzer->p_st_session, '\0', sizeof(L7_session));
    }

    //初始化 transaction
    if(p_st_l7_analyzer->p_st_transaction == NULL)
    {
        p_st_l7_analyzer->p_st_transaction = (L7_transaction*) frmalloc(sizeof(L7_transaction));
        if(p_st_l7_analyzer->p_st_transaction)
            memset(p_st_l7_analyzer->p_st_transaction, '\0', sizeof(L7_transaction));
    }

    p_st_l7_analyzer->p_st_l7_module = p_st_l7_module;
    if(p_st_l7_analyzer->p_st_session == NULL || p_st_l7_analyzer->p_st_transaction==NULL)
    {
        FRLOG_ERROR_DETAIL("frmalloc err");
        goto FAIL;
    }
    __sync_fetch_and_add(&p_st_l7_analyzer->p_st_l7_module->ul_refcnt, 1);

    return FR_SUCC;

FAIL:
    p_st_l7_analyzer->p_st_l7_module = NULL;
    if(p_st_l7_analyzer->p_st_session)
        frfree(p_st_l7_analyzer->p_st_session);
    p_st_l7_analyzer->p_st_session = NULL;
    if(p_st_l7_analyzer->p_st_transaction)
        frfree(p_st_l7_analyzer->p_st_transaction);
    p_st_l7_analyzer->p_st_transaction = NULL;
    return FR_FAIL;

}

/*******************************************************************************
*  FUNC     :  L7_analyzer节点内存释放
*  ARGS     :  p_st_node  (Frlist_node *)
*  RTN      :
*  NOTE     :  需要lock
*******************************************************************************/
void frct_l7analyzer_free(L7_analyzer *p_st_l7_analyzer)
{
    if(p_st_l7_analyzer == NULL)
        return;

    if(p_st_l7_analyzer->p_st_session)
    {
        frl7session_free(p_st_l7_analyzer->p_st_l7_module, p_st_l7_analyzer->p_st_session);
        frfree(p_st_l7_analyzer->p_st_session);
    }

    if(p_st_l7_analyzer->p_st_transaction)
    {
        frl7trans_free(p_st_l7_analyzer->p_st_transaction);
        frfree(p_st_l7_analyzer->p_st_transaction);
    }
    if(p_st_l7_analyzer->p_st_l7_module)
    {
        __sync_fetch_and_sub(&p_st_l7_analyzer->p_st_l7_module->ul_refcnt, 1);
        p_st_l7_analyzer->p_st_l7_module = NULL;
    }

    memset(p_st_l7_analyzer, 0x00, sizeof(L7_analyzer));
    return;
}


/*******************************************************************************
*  FUNC     :  设置l4信息
*  ARGS     :  p_st_ct      (Conntrack *)
*           :  ui_src       (原地址)
*           :  us_sport     (原端口)
*           :  ui_data_len      (数据长度)
*           :  st_timeval_pkg   (数据包发生时间)
*  RTN      :
*  NOTE     :  该函数必须要在ct->ip,port设置好后，才能使用
*******************************************************************************/
FR_STATIC void __frct_set_l4_transaction(Conntrack *p_st_ct
                                    , u_int ui_src, u_short us_sport
                                    , unsigned int ui_data_len
                                    , struct timeval st_timeval_pkg)
{
    int i_cs_direct = FR_DIRECT_UNKNOWN;

    if(p_st_ct == NULL)
        return;

    i_cs_direct = frcap_get_cs_direction(p_st_ct, ui_src, us_sport);
    if(i_cs_direct == FR_DIRECT_UNKNOWN)
        return;

    //request
    if(i_cs_direct == FR_CLIENT_IP_PORT)
    {
        p_st_ct->st_l4_transaction.l_request_bytes += ui_data_len;
        //第1次 或者从 response转到request
        if( (p_st_ct->st_l4_transaction.st_request_time_first_byte.tv_sec == 0L
             && p_st_ct->st_l4_transaction.st_request_time_first_byte.tv_usec == 0L)
             ||
             (p_st_ct->st_l4_transaction.st_response_time_first_byte.tv_sec != 0L
              || p_st_ct->st_l4_transaction.st_response_time_first_byte.tv_usec != 0L) )
            p_st_ct->st_l4_transaction.st_request_time_first_byte = st_timeval_pkg;

        p_st_ct->st_l4_transaction.st_request_time = st_timeval_pkg;
        //清除response数据
        p_st_ct->st_l4_transaction.st_response_time_first_byte.tv_sec = 0;
        p_st_ct->st_l4_transaction.st_response_time_first_byte.tv_usec = 0;
    }
    else //response
    {
        p_st_ct->st_l4_transaction.l_response_bytes += ui_data_len;
        //第1次
        if( p_st_ct->st_l4_transaction.st_response_time_first_byte.tv_sec == 0L
             && p_st_ct->st_l4_transaction.st_response_time_first_byte.tv_usec == 0L)
            p_st_ct->st_l4_transaction.st_response_time_first_byte = st_timeval_pkg;

        p_st_ct->st_l4_transaction.st_response_time = st_timeval_pkg;
    }

    return;
}

/*******************************************************************************
*  FUNC     :  设置l4信息
*  ARGS     :  p_st_ct      (Conntrack *)
*           :  p_src_mac (原mac)
*           :  p_dst_mac (目的mac)
*           :  us_proto  (协议 tcp/udp)
*           :  ui_src    (原地址)
*           :  us_sport  (原端口)
*           :  ui_dest   (目的地址)
*           :  us_dport  (目的端口)
*           :  i_status  (连接状态: APM_CONNECT_SYN_SENT / APM_CONNECT_SYN_RECV / APM_CONNECT_ESTABLISHED / APM_CONNECT_CLOSED / APM_CONNECT_TIMEOUT)
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC void __frct_set_mac_ip_direct(Conntrack *p_st_ct
                                      , const char *p_src_mac, const char *p_dst_mac
                                      , u_short us_proto
                                      , u_int ui_src, u_short us_sport, u_int ui_dest, u_short us_dport
                                      , int i_status)
{
    if(p_st_ct == NULL)
        return;

    p_st_ct->ip_proto = us_proto;

    if(us_proto == IPPROTO_UDP)
    {
        p_st_ct->flg_direct_known = FR_DIRECT_UNKNOWN;
        if(p_src_mac)
            memcpy(p_st_ct->src_mac, p_src_mac, APM_MAC_SIZE);
        if(p_dst_mac)
            memcpy(p_st_ct->dst_mac, p_dst_mac, APM_MAC_SIZE);
        p_st_ct->src_ip = ui_src;
        p_st_ct->src_port = us_sport;
        p_st_ct->dst_ip = ui_dest;
        p_st_ct->dst_port = us_dport;
    }
    else if(us_proto == IPPROTO_TCP)
    {
        if(i_status== APM_CONNECT_SYN_SENT || i_status == APM_CONNECT_SYN_RECV)
            p_st_ct->flg_direct_known = FR_DIRECT_KNOWN;
        else
            p_st_ct->flg_direct_known = FR_DIRECT_UNKNOWN;

        if(i_status== APM_CONNECT_SYN_RECV) //反向
        {
            if(p_dst_mac)
                memcpy(p_st_ct->src_mac, p_dst_mac, APM_MAC_SIZE);
            if(p_src_mac)
                memcpy(p_st_ct->dst_mac, p_src_mac, APM_MAC_SIZE);
            p_st_ct->src_ip = ui_dest;
            p_st_ct->src_port = us_dport;
            p_st_ct->dst_ip = ui_src;
            p_st_ct->dst_port = us_sport;
        }
        else //正向或不明
        {
            if(p_src_mac)
                memcpy(p_st_ct->src_mac, p_src_mac, APM_MAC_SIZE);
            if(p_dst_mac)
                memcpy(p_st_ct->dst_mac, p_dst_mac, APM_MAC_SIZE);
            p_st_ct->src_ip = ui_src;
            p_st_ct->src_port = us_sport;
            p_st_ct->dst_ip = ui_dest;
            p_st_ct->dst_port = us_dport;
        }
    }
    else
    {
        p_st_ct->flg_direct_known = FR_DIRECT_UNKNOWN;
        if(p_src_mac)
            memcpy(p_st_ct->src_mac, p_src_mac, APM_MAC_SIZE);
        if(p_dst_mac)
            memcpy(p_st_ct->dst_mac, p_dst_mac, APM_MAC_SIZE);
        p_st_ct->src_ip = ui_src;
        p_st_ct->src_port = us_sport;
        p_st_ct->dst_ip = ui_dest;
        p_st_ct->dst_port = us_dport;
    }
    return;
}

/*******************************************************************************
*  FUNC     :  处理关联连接
*  ARGS     :  p_st_ct   (Conntrack *)
*           :  us_proto  (协议 tcp/udp)
*           :  ui_dest   (目的地址)
*           :  us_dport  (目的端口)
*           :  st_timeval_pkg   (数据包发生时间)
*           :  p_st_report_data (数据保存到该结构体)
*  RTN      :  FR_YES 查询到关联连接; FR_NO :没有查询到关联连接
*  NOTE     :  线程不安全
*******************************************************************************/
FR_STATIC int __frct_do_expect(Conntrack *p_st_ct
                              , u_short us_proto
                              , u_int ui_src
                              , u_short us_sport
                              , u_int ui_dest
                              , u_short us_dport)
{
    Frexp *p_st_exp=NULL;

    if( p_st_ct==NULL || p_st_ct->flg_has_master == FR_YES)
        return FR_NO;

    //查询关联连接
    p_st_exp = frexpect_get(us_proto, ui_src, us_sport, ui_dest, us_dport);
    if(p_st_exp)
        goto EXP_FOUND;

    p_st_exp = frexpect_get(us_proto, ui_dest, us_dport, ui_src, us_sport);
    if(p_st_exp)
        goto EXP_FOUND;

    p_st_exp = frexpect_get(us_proto, ui_src, 0, ui_dest, us_dport);
    if(p_st_exp)
        goto EXP_FOUND;

    p_st_exp = frexpect_get(us_proto, ui_dest, 0, ui_src, us_sport);
    if(p_st_exp == NULL)
    {
        //if(frexpect_count() > 0)
        //    FRPRINT_L4(us_proto, p_st_ct->src_ip, p_st_ct->src_port, p_st_ct->dst_ip, p_st_ct->dst_port, 0, 0);
        return FR_NO;
    }

EXP_FOUND:
    //frwarn("frct_expect_get found\n");
    //设置关联连接

    p_st_ct->st_masterInfo.src_ip = p_st_exp->master_src_ip;
    p_st_ct->st_masterInfo.src_port = p_st_exp->master_src_port;
    p_st_ct->st_masterInfo.dst_ip = p_st_exp->master_dst_ip;
    p_st_ct->st_masterInfo.dst_port = p_st_exp->master_dst_port;
    p_st_ct->st_masterInfo.p_st_l7_module = p_st_exp->p_master_l7_module;
    p_st_ct->st_masterInfo.p_lst_hdr_param = p_st_exp->p_lst_hdr_param;

    p_st_ct->st_masterInfo.exp_src_ip = p_st_exp->ui_src_ip;
    p_st_ct->st_masterInfo.exp_src_port = p_st_exp->us_src_port;
    p_st_ct->st_masterInfo.exp_dst_ip = p_st_exp->ui_dst_ip;
    p_st_ct->st_masterInfo.exp_dst_port = p_st_exp->us_dst_port;

    if(p_st_ct->flg_chg_stat_ip == 0)
    {
        p_st_ct->stat_src_port = p_st_exp->stat_src_port;
        p_st_ct->stat_dst_port = p_st_exp->stat_dst_port;
    }

    //获取强行指定的l7_module
    L7_module *p_st_module= frmodule_get_l7module(p_st_exp->i_l7_proto);
    if(p_st_module)
    {
        frct_l7proto_update(p_st_ct, p_st_module);
        frct_l7proto_change_status(p_st_ct, APM_MODULE_STATUS_DEFINE, 0, APM_L7PROTO_STATUS_DONE);
        frmodule_put_l7module(p_st_module);
    }

    p_st_ct->flg_has_master = FR_YES;
    frfree(p_st_exp);
    return FR_YES;
}

/*******************************************************************************
*  FUNC     :  按连接数统计
*  ARGS     :  i_flag_has_exp       (FR_YES:有设置关联连接; FR_NO:没有设置关联连接)
*           :  p_st_ct              (Conntrack *)
*           :  i_tx_bytes           (tx bytes)
*           :  i_rx_bytes           (rx bytes)
*  RTN      :
*  NOTE     :  有关联连接发生时，原来的连接不进行删除
*           :  web端统计时,未使用该信息
*******************************************************************************/
FR_STATIC void __frct_stat_conns(int i_flag_has_exp, Conntrack *p_st_ct, int i_tx_bytes, int i_rx_bytes)
{
    if(p_st_ct == NULL)
        return;

    //还没有记录过连接信息
    if(p_st_ct->flg_report_conn_info == FR_NO)
    {
        if(p_st_ct->stat_dst_port)//有关联连接
            frstat_conns_add_conn_ct(p_st_ct, FR_YES, p_st_ct->src_ip, p_st_ct->dst_ip, p_st_ct->stat_dst_port, 1, i_tx_bytes, i_rx_bytes);
        else
            frstat_conns_add_conn_ct(p_st_ct, FR_YES, p_st_ct->src_ip, p_st_ct->dst_ip, p_st_ct->dst_port, 1, i_tx_bytes, i_rx_bytes);
        return;
    }

    //记录过连接信息 && 没有关联连接
    if(p_st_ct->flg_report_conn_info == FR_YES)
    {
        //case2: 正常ip -> 关联ip
        //其他情况不考虑
        if(p_st_ct->stat_dst_port)
        {
            //{
            //    char arr_buff[128];
            //    frl4_print2(arr_buff, sizeof(arr_buff), p_st_ct->ip_proto, p_st_ct->src_ip, p_st_ct->src_port, p_st_ct->dst_ip, p_st_ct->stat_dst_port);
            //    FRLOG_ERROR_DETAIL("%s ",arr_buff);
            //}
            frstat_conns_add_conn_ct(p_st_ct, FR_YES, p_st_ct->src_ip, p_st_ct->dst_ip, p_st_ct->stat_dst_port, 1, i_tx_bytes, i_rx_bytes);
        }
        else
            frstat_conns_add_traffic(p_st_ct, i_tx_bytes, i_rx_bytes);
    }
    return;
}

/*******************************************************************************
*  FUNC     :  添加ct
*  ARGS     :  p_src_mac (原mac)
*           :  p_dst_mac (目的mac)
*           :  us_proto  (协议 tcp/udp)
*           :  ui_src    (原地址)
*           :  us_sport  (原端口)
*           :  ui_dest   (目的地址)
*           :  us_dport  (目的端口)
*           :  i_flag_insert_task   (是否增加任务到工作队列)
*           :  em_analyz_cmd        (增加到工作队列任务的类型)
*           :  i_flag_get_ct        (是否需要返回ct, FR_YES(1):返回ct;FR_NO(0):返回值为NULL)
*           :  i_status             (连接状态: APM_CONNECT_SYN_SENT / APM_CONNECT_SYN_RECV / APM_CONNECT_ESTABLISHED / APM_CONNECT_CLOSED / APM_CONNECT_TIMEOUT)
*           :  p_data               (数据)
*           :  ui_data_len          (数据长度)
*           :  st_timeval_pkg       (数据包发生时间)
*           :  i_flag_drop_timestamp_incorrect       (是否丢弃时间不正确的数据包 1:丢弃 0:不丢弃)
*           :  i_flag_ssl_shm       (是否为ssl解码后的数据 1:是 0:不是)
*  RTN      :  i_flag_get_ct=1时，返回ct
*  NOTE     :  返回conntrack时,refcnt++
*******************************************************************************/
Conntrack *frct_add(const char *p_src_mac, const char *p_dst_mac
                  , u_short us_proto, u_int ui_src, u_short us_sport, u_int ui_dest, u_short us_dport
                  , int i_flag_insert_task, Franalyz_cmd em_analyz_cmd
                  , int i_flag_get_ct, int i_status
                  , char *p_data, unsigned int ui_data_len, struct timeval st_timeval_pkg
                  , int i_flag_drop_timestamp_incorrect
                  , int i_flag_ssl_shm)
{
    Frlist_node *p_st_node=NULL;
    Conntrack *p_st_ct=NULL;
    char arr_hash_key[20];
    int i_hash_key_len=0, i_ret_fun=0;
    int i_rx_bytes=0, i_tx_bytes=0, i_rx_pkts=0, i_tx_pkts=0;

    //FRPRINT_L4(us_proto, ui_src, us_sport, ui_dest, us_dport);
    //FRPRINT_BIN("frct_add", p_data, ui_data_len);

    //判断是否为ssl的连接
    //ssl的数据包在nids传输过来时不做处理，等解密进程解密后传输到共享内存来才处理
    if (i_flag_ssl_shm == 0 && us_proto == IPPROTO_TCP)
    {
        frssl_read_lock();
        if (frssl_config_lookup(ui_dest, us_dport))
        {
            frssl_read_unlock();
            return NULL;
        }
        if (frssl_config_lookup(ui_src, us_sport))
        {
            frssl_read_unlock();
            return NULL;
        }
        frssl_read_unlock();
    }

    frct_hash_rlock();
    p_st_node = __frct_find(us_proto, ui_src, us_sport, ui_dest, us_dport);

    //数据包超过缓存个数，并且不是fin包时，丢弃
    if( (st_apm_params.ui_ct_data_pkgs >0 && g_ul_pcap_queue_used >= st_apm_params.ui_ct_data_pkgs)
        && ( (p_st_node && i_status != APM_CONNECT_CLOSED) || p_st_node == NULL) )
    {
        if(p_st_node)
        {
            p_st_ct = (Conntrack *)FRLIST_GET_NODE_DATA(p_st_node);
            if(p_st_ct)
            {
                p_st_ct->st_connection_lastskb_time = st_timeval_pkg;
                p_st_ct->ul_drop_packets++;
            }
        }
        //queue队列超出，丢弃该包
        //FRLOG_WARN_TIME("queue is over. drop packet(packet max=%lu)", st_apm_params.ui_ct_data_pkgs);
        frct_hash_unrlock();
        frstat_system_add(1, ui_data_len, 0, 1);
        frstat_pkg_add_frame_drop(1);
        //FRLOG_DEBUG_DETAIL("frame_drop pcap_queue_used=%lu ct_data_pkgs=%lu p_st_node=%p"
        //        , g_ul_pcap_queue_used, st_apm_params.ui_ct_data_pkgs
        //        , p_st_node);
        return NULL;
    }

    //超过连接数时，丢弃新建连接
    if(p_st_node == NULL && g_ul_ct_used >= st_apm_params.ui_ct_max_cnt)
    {
        //queue队列超出，丢弃该包
        frct_hash_unrlock();
        FRLOG_WARN_TIME("ct is over. drop packet(ct max=%lu)", st_apm_params.ui_ct_max_cnt);
        frstat_system_add(1, ui_data_len, 0, 1);
        frstat_pkg_add_frame_drop(1);
        FRLOG_DEBUG_DETAIL("frame_drop ct_used=%u ct_max_cnt=%u", g_ul_ct_used, st_apm_params.ui_ct_max_cnt);
        return NULL;
    }

    //丢弃时间戳不正确的数据包
    if(i_flag_drop_timestamp_incorrect && p_st_node)
    {
        int i_is_drop = 0;
        p_st_ct = (Conntrack *)FRLIST_GET_NODE_DATA(p_st_node);
        if(p_st_ct && p_st_ct->st_connection_lastskb_time.tv_sec != 0)
        {
            if(p_st_ct->st_connection_lastskb_time.tv_sec > st_timeval_pkg.tv_sec)
                i_is_drop = 1;
            else if(p_st_ct->st_connection_lastskb_time.tv_sec == st_timeval_pkg.tv_sec
                    && p_st_ct->st_connection_lastskb_time.tv_usec > st_timeval_pkg.tv_usec)
                i_is_drop = 1;

            if(i_is_drop)
            {
                p_st_ct->st_connection_lastskb_time = st_timeval_pkg;
                p_st_ct->ul_drop_packets++;
                frct_hash_unrlock();
                frstat_system_add(1, ui_data_len, 0, 1);
                frstat_pkg_add_frame_drop(1);
                FRLOG_WARN_TIME("packet's timestamp is incorrect");
                return NULL;
            }
        }
    }

    //连接已经存在
    if(p_st_node)
    {
        frstat_system_add(1, ui_data_len, 0, 0);
        //frdbg("find old ct(%02d %s:%i->%s:%i)\n", us_proto, int_ntoa(ui_src), us_sport, int_ntoa(ui_dest), us_dport);
        p_st_ct = (Conntrack *)FRLIST_GET_NODE_DATA(p_st_node);
        pthread_spin_lock(&p_st_ct->lock);

        //丢弃不需要处理的数据包
        if(p_st_ct->flg_drop_pcap == 1)
        {
            p_st_ct->st_connection_lastskb_time = st_timeval_pkg;
            p_st_ct->ul_drop_packets++;
            pthread_spin_unlock(&p_st_ct->lock);
            frct_hash_unrlock();
            frstat_system_add(0, 0, 0, 1);
            frstat_pkg_add_frame_drop(1);
            //FRLOG_DEBUG_DETAIL("frame_drop p_st_ct->flg_status == APM_CONNECT_CLOSED");
            return NULL;
        }

        //ct在删除状态时，数据包丢弃(短时间内，相同ct不能重复出现)
        #ifdef FRDBG_DROP_REPEAT_CT
        if(p_st_ct->flg_status == APM_CONNECT_CLOSED)
        {
            p_st_ct->st_connection_lastskb_time = st_timeval_pkg;
            p_st_ct->ul_drop_packets++;
            pthread_spin_unlock(&p_st_ct->lock);
            frct_hash_unrlock();
            frstat_system_add(0, 0, 0, 1);
            frstat_pkg_add_frame_drop(1);
            //FRLOG_DEBUG_DETAIL("frame_drop p_st_ct->flg_status == APM_CONNECT_CLOSED");
            return NULL;
        }
        #endif

        p_st_ct->flg_status = i_status;
        p_st_ct->flg_update = FR_YES;

        //僵尸连接->活跃连接
        if(p_st_ct->flg_report_conn_dead == FR_YES)
        {
            if(__frct_check_dead_conn(p_st_ct, &st_timeval_pkg, -1) == FR_SUCC)
                p_st_ct->flg_report_conn_dead = FR_NO;
        }

        if(i_flag_get_ct==FR_YES)
            __sync_fetch_and_add(&p_st_ct->refcnt,1);
        if( p_st_ct->st_connection_established.tv_sec == 0L
            && p_st_ct->st_connection_established.tv_usec == 0L )
        {
            if(i_status == APM_CONNECT_ESTABLISHED )
                p_st_ct->st_connection_established = st_timeval_pkg;
            else if (ui_data_len > 0 )
                p_st_ct->st_connection_established = p_st_ct->st_connection_created;
        }

        p_st_ct->st_connection_lastskb_time = st_timeval_pkg;
        //l4 transation
        __frct_set_l4_transaction(p_st_ct, ui_src, us_sport, ui_data_len, st_timeval_pkg);

        //数据包加入到工作队列
        if(i_flag_insert_task == FR_YES)
            __frcap_insert_task(p_st_ct, us_proto, ui_src, us_sport, ui_dest, us_dport
                              , em_analyz_cmd, p_data, ui_data_len, APM_L7_UNKNOWN, st_timeval_pkg);
        if( frcap_get_cs_direction(p_st_ct, ui_src, us_sport) == FR_CLIENT_IP_PORT)
        {
            p_st_ct->ul_rx_packets++;
            p_st_ct->ul_rx_bytes += ui_data_len;
            i_tx_bytes = 0;
            i_tx_pkts = 0;
            i_rx_bytes = ui_data_len;
            i_rx_pkts = 1;
        }
        else
        {
            p_st_ct->ul_tx_packets++;
            p_st_ct->ul_tx_bytes += ui_data_len;
            i_tx_bytes = ui_data_len;
            i_tx_pkts = 1;
            i_rx_bytes = 0;
            i_rx_pkts = 0;
        }

        //设置关联连接
        i_ret_fun = __frct_do_expect(p_st_ct, us_proto, ui_src, us_sport, ui_dest, us_dport);
        //连接信息统计
        __frct_stat_conns(i_ret_fun, p_st_ct, i_tx_bytes, i_rx_bytes);
        frstat_ports_add_stat(p_st_ct->ip_proto, p_st_ct->dst_ip, p_st_ct->dst_port, i_rx_bytes, i_tx_bytes, i_tx_pkts, i_rx_pkts);

        pthread_spin_unlock(&p_st_ct->lock);
        frct_hash_unrlock();
        return i_flag_get_ct==FR_YES?p_st_ct:NULL;
    }

    //ct条数限制
    if(st_apm_params.ui_ct_max_cnt >0 && g_ul_ct_used >= st_apm_params.ui_ct_max_cnt)
    {
        frct_hash_unrlock();
        FRLOG_WARN_TIME("ct counts is over. drop packet(max=%lu)", st_apm_params.ui_ct_max_cnt);
        frstat_pkg_add_frame_drop(1);
        FRLOG_DEBUG_DETAIL("frame_drop ct_used=%lu ct_max_cnt=%u", g_ul_ct_used, st_apm_params.ui_ct_max_cnt);
        return NULL;
    }

    //建立新连接
    p_st_ct = (Conntrack*) frmalloc(sizeof(Conntrack));
    if(!p_st_ct)
    {
        frct_hash_unrlock();
        FRLOG_ERROR_DETAIL("frmalloc err");
        return NULL;
    }
    memset(p_st_ct, 0x00, sizeof(Conntrack));

    fruuid(p_st_ct->arr_connection_id);
    pthread_spin_init(&p_st_ct->lock, 0);
    
    p_st_ct->p_st_hash_detect_data = frct_detect_hash_init(APM_CT_DETECT_HASH_SIZE);

    p_st_ct->refcnt = i_flag_get_ct==FR_YES?1:0;
    p_st_ct->flg_update = FR_NO;
    p_st_ct->flg_status = i_status;
    p_st_ct->flg_inTask = FR_NO;
    p_st_ct->flg_report_conn_info = FR_NO;
    p_st_ct->flg_report_conn_dead = FR_NO;
    p_st_ct->i_flag_l7proto_status = APM_L7PROTO_STATUS_UNKNOWN;
    __frct_set_mac_ip_direct(p_st_ct, p_src_mac, p_dst_mac, us_proto, ui_src, us_sport, ui_dest, us_dport, i_status);
    p_st_ct->flg_chg_direct = FR_NO;

    //连接时间设定
    p_st_ct->st_connection_created = st_timeval_pkg;
    if(i_status == APM_CONNECT_ESTABLISHED || ui_data_len > 0)
        p_st_ct->st_connection_established = st_timeval_pkg;
    p_st_ct->st_connection_lastskb_time = st_timeval_pkg;
    p_st_ct->p_st_timer_entry = (Timer_entry*) frmalloc(sizeof(Timer_entry));
    if(p_st_ct->p_st_timer_entry)
        memset(p_st_ct->p_st_timer_entry, 0x00, sizeof(Timer_entry));

    //l4 transation
    __frct_set_l4_transaction(p_st_ct, ui_src, us_sport, ui_data_len, st_timeval_pkg);

    frlist_header_init(&p_st_ct->lst_cap_queue);
    p_st_ct->ui_cap_peek_num =0;
    p_st_ct->flg_cap_peek_end = FR_NO;
    p_st_ct->ui_done_data_cnt =0;
    p_st_ct->ui_detect_data_cnt[0] =0;
    p_st_ct->ui_detect_data_cnt[1] =0;
    p_st_ct->flg_cap_redo = FR_NO;
    p_st_ct->i_flag_module_status = APM_MODULE_STATUS_UNKNOWN;
    p_st_ct->ui_define_ver = 0;
    __frct_l7analyzer_init(&p_st_ct->st_l7_analyzer);

    p_st_ct->ul_rx_packets++;
    p_st_ct->ul_rx_bytes += ui_data_len;

    //检查数据是否正确
    if(p_st_ct->p_st_timer_entry == NULL)
    {
        FRLOG_ERROR_DETAIL("new ct err");
        __frct_free(p_st_ct);
        frct_hash_unrlock();
        return NULL;
    }

    __sync_fetch_and_add(&g_ul_ct_used, 1);

    //frdbg("new ct\n");
    //设置关联连接
    __frct_do_expect(p_st_ct, us_proto, ui_src, us_sport, ui_dest, us_dport);

    //连接数统计
    p_st_ct->flg_report_conn_info = FR_NO;
    i_tx_bytes = 0;
    i_tx_pkts = 1;
    i_rx_bytes = ui_data_len;
    i_rx_pkts = 0;
    if(p_st_ct->stat_dst_port)
        frstat_conns_add_conn_ct(p_st_ct, FR_YES, p_st_ct->src_ip, p_st_ct->dst_ip, p_st_ct->stat_dst_port, 1, i_tx_bytes, i_rx_bytes);
    else
        frstat_conns_add_conn_ct(p_st_ct, FR_YES, p_st_ct->src_ip, p_st_ct->dst_ip, p_st_ct->dst_port, 1, i_tx_bytes, i_rx_bytes);
    frstat_ports_add_stat(p_st_ct->ip_proto, p_st_ct->dst_ip, p_st_ct->dst_port, i_rx_bytes, i_tx_bytes, i_tx_pkts, i_rx_pkts);

    //实际数据不会发生错误
    i_hash_key_len = frct_hash_key(us_proto, ui_src, us_sport, ui_dest, us_dport, arr_hash_key, sizeof(arr_hash_key));
    p_st_node = frct_hash_add(p_st_ct, arr_hash_key, i_hash_key_len);

    //节点加入失败
    if(p_st_node == NULL)
    {
        FRLOG_ERROR_DETAIL("frct_hash_add fail");
        __sync_fetch_and_sub(&g_ul_ct_used, 1);
        __frct_free(p_st_ct);
        i_flag_get_ct = FR_NO;
        goto CT_NEW_END;
    }
    
    frstat_system_add(1, ui_data_len, 1, 0);

    //数据包加入到工作队列
    if(i_flag_insert_task == FR_YES)
    {
        pthread_spin_lock(&p_st_ct->lock);
        __frcap_insert_task(p_st_ct, us_proto, ui_src, us_sport, ui_dest, us_dport
                          , em_analyz_cmd, p_data, ui_data_len, APM_L7_UNKNOWN, st_timeval_pkg);
        pthread_spin_unlock(&p_st_ct->lock);
    }

    //激活时钟
    p_st_ct->p_st_timer_entry->st_delay.tv_sec = APM_CFG_MIN_TIMEOUT;
    p_st_ct->p_st_timer_entry->st_delay.tv_usec = 0;
    p_st_ct->p_st_timer_entry->p_st_event = frtimer_new(g_p_st_ebase, __frct_timer_callback, p_st_node, &p_st_ct->p_st_timer_entry->st_delay);
    if(p_st_ct->p_st_timer_entry->p_st_event == NULL)
    {
        FRLOG_ERROR_DETAIL("frtimer_new err");
        __sync_fetch_and_sub(&g_ul_ct_used, 1);

        frct_hash_del(p_st_node);
        __frct_free(p_st_ct);
        i_flag_get_ct = FR_NO;
    }

CT_NEW_END:
    frct_hash_unrlock();
    return i_flag_get_ct==FR_YES?p_st_ct:NULL;
}

/*******************************************************************************
*  FUNC     :  ct使用的内存等资源释放
*  ARGS     :  *p_st_ct (Conntrack)
*  RTN      :
*  NOTE     :
*******************************************************************************/
FR_STATIC void __frct_free(Conntrack *p_st_ct)
{
    int i_cap_cnts=0;

    if(p_st_ct == NULL)
        return;
    //printf("__frct_free(ct->queue=%d)\n", frlist_size(&p_st_ct->lst_cap_queue));
    i_cap_cnts = frlist_size(&p_st_ct->lst_cap_queue);
    __sync_fetch_and_sub(&g_ul_pcap_queue_used, i_cap_cnts);
    frlist_remove_all(&p_st_ct->lst_cap_queue, frcap_node_free);

    if(p_st_ct->st_masterInfo.p_st_l7_module)
        __sync_fetch_and_sub(&p_st_ct->st_masterInfo.p_st_l7_module->ul_refcnt, 1);
    if(p_st_ct->st_masterInfo.p_lst_hdr_param)
        frexpect_free_param(&p_st_ct->st_masterInfo.p_lst_hdr_param);

    frct_l7proto_destory(p_st_ct);
    if(p_st_ct->p_st_timer_entry)
    {
        if(p_st_ct->p_st_timer_entry->p_st_event)
        {
            frtimer_free(p_st_ct->p_st_timer_entry->p_st_event);
            p_st_ct->p_st_timer_entry->p_st_event = NULL;
        }
        frfree(p_st_ct->p_st_timer_entry);
        p_st_ct->p_st_timer_entry = NULL;
    }

    //释放lua信息
    if(p_st_ct->st_luaInfo.p_lua_cfg)
        frlua_config_put((Lua_cfg *)p_st_ct->st_luaInfo.p_lua_cfg);

    //释放协议多次判断的缓存信息
    frct_detect_hash_free(&p_st_ct->p_st_hash_detect_data);

    pthread_spin_destroy(&p_st_ct->lock);
    frfree(p_st_ct);
    __sync_fetch_and_sub(&g_ul_ct_used, 1);
    
    return;
}

/*******************************************************************************
*  FUNC     :  ct从hash表里删除
*  ARGS     :  *p_st_ct           (Conntrack)
            :  p_hash_key      (hash key，如果没有设置为NULL)
            :  i_hash_key_len  (hash key长度，如果没有设置为0)
*  RTN      :
*  NOTE     : 如果conntrack里refcnt不为0，则不释放
            : ct内存不在本函数删除
*******************************************************************************/
FR_STATIC void __frct_del(Frlist_node *p_st_node)
{
    Conntrack *p_ct_del=NULL;

    if(p_st_node==NULL)
        return;

    p_ct_del = (Conntrack *)FRLIST_GET_NODE_DATA(p_st_node);
    if(p_ct_del && p_ct_del->refcnt > 0)
    {
        FRLOG_WARN_DETAIL("p_ct->refcnt > 0");
        return;
    }

    if( g_i_flag_run && p_ct_del->flg_report_conn_info == FR_YES)
    {
        char arr_buff[128];
        int i_l7_proto =-1;

        if(p_ct_del->st_l7_analyzer.p_st_l7_module)
            i_l7_proto = p_ct_del->st_l7_analyzer.p_st_l7_module->i_l7_proto;
        frl4_print2(arr_buff, sizeof(arr_buff), p_ct_del->ip_proto
                , p_ct_del->src_ip, p_ct_del->src_port, p_ct_del->dst_ip, p_ct_del->dst_port);
        FRLOG_ERROR_DETAIL("src_check: no report close info[%s] module_status=%d l7proto_status=%d l7=%d", arr_buff
                , p_ct_del->i_flag_module_status, p_ct_del->i_flag_l7proto_status, i_l7_proto);
    }

    frct_hash_del(p_st_node);
    return;
}

/*******************************************************************************
*  FUNC     :  ct节点删除
*  ARGS     :  us_proto         (协议)
            :  ui_src           (原地址)
            :  us_sport         (原端口)
            :  ui_dest          (目的地址)
            :  us_dport         (目的端口)
*           :  st_timeval_pkg   (数据包发生时间)
*  RTN      :
*  NOTE     :  调整timeout时钟,通过时钟函数实现删除ct
*******************************************************************************/
void frct_del(u_short us_proto, u_int ui_src, u_short us_sport, u_int ui_dest, u_short us_dport
            , Franalyz_cmd em_analyz_cmd, struct timeval st_timeval_pkg)
{
    Frlist_node *p_st_node=NULL;
    Conntrack *p_st_ct=NULL;

    frssl_read_lock();
    if (frssl_config_lookup(ui_dest, us_dport))
    {
        frssl_read_unlock();
        return;
    }
    if (frssl_config_lookup(ui_src, us_sport))
    {
        frssl_read_unlock();
        return;
    }
    frssl_read_unlock();

    frct_hash_wlock();
    p_st_node = frct_hash_get(us_proto, ui_src, us_sport, ui_dest, us_dport);
    if(!p_st_node)
        p_st_node = frct_hash_get(us_proto, ui_dest, us_dport, ui_src, us_sport);
    if(!p_st_node)
    {
        //frdbg("node not found\n");
        frct_hash_unwlock();
        return;
    }

    p_st_ct = (Conntrack *)FRLIST_GET_NODE_DATA(p_st_node);
    pthread_spin_lock(&p_st_ct->lock);
    //删除连续多个fin包的情况
    if(p_st_ct->flg_status == APM_CONNECT_CLOSED)
    {
        pthread_spin_unlock(&p_st_ct->lock);
        frct_hash_unwlock();
        return;
    }

    if(p_st_ct->flg_report_conn_info == FR_YES)
        frstat_conns_del_conn_ct(p_st_ct, FR_NO);

    p_st_ct->flg_status = APM_CONNECT_CLOSED;
    p_st_ct->st_connection_close_time = st_timeval_pkg;
    __frcap_insert_task(p_st_ct, us_proto, ui_src, us_sport, ui_dest, us_dport
                      , em_analyz_cmd, NULL, 0, APM_L7_UNKNOWN, st_timeval_pkg);
    pthread_spin_unlock(&p_st_ct->lock);

    //p_st_ct->p_st_timer_entry->st_delay.tv_sec = APM_TIMEOUT_DEL_SEC;
    //p_st_ct->p_st_timer_entry->st_delay.tv_usec = 0;
    //frtimer_reload(p_st_ct->p_st_timer_entry->p_st_event, &p_st_ct->p_st_timer_entry->st_delay);

    frct_hash_unwlock();
    return;
}

/*******************************************************************************
*  FUNC     :  查找ct节点
*  ARGS     :  us_proto  (协议 tcp/udp)
            :  ui_src    (原地址)
            :  us_sport  (原端口)
            :  ui_dest   (目的地址)
            :  us_dport  (目的端口)
*  RTN      :  查询到返回包含ct的节点
            :  未查询到返回 NULL
*  NOTE     :
*******************************************************************************/
FR_STATIC Frlist_node *__frct_find(u_short us_proto, u_int ui_src, u_short us_sport, u_int ui_dest, u_short us_dport)
{
    Frlist_node *p_st_node=NULL;

    p_st_node = frct_hash_get(us_proto, ui_src, us_sport, ui_dest, us_dport);
    if(!p_st_node)
    {
        p_st_node = frct_hash_get(us_proto, ui_dest, us_dport, ui_src, us_sport);
    }

    return p_st_node;
}

/*******************************************************************************
*  FUNC     :  获取ct节点
*  ARGS     :  us_proto  (协议)
            :  ui_src    (原地址)
            :  us_sport  (原端口)
            :  ui_dest   (目的地址)
            :  us_dport  (目的端口)
*  RTN      :  成功返回 Frlist_node *; 否则返回NULL
*  NOTE     :  注意该函数增加 引用次数
*******************************************************************************/
Conntrack *frct_get(u_short us_proto, u_int ui_src, u_short us_sport, u_int ui_dest, u_short us_dport)
{
    Frlist_node *p_st_node=NULL;
    Conntrack *p_st_ct=NULL;

    frct_hash_wlock();
    p_st_node = __frct_find(us_proto, ui_src, us_sport, ui_dest, us_dport);

    if(p_st_node)
    {
        p_st_ct = (Conntrack *)FRLIST_GET_NODE_DATA(p_st_node);
        __sync_fetch_and_add(&p_st_ct->refcnt,1);
    }
    frct_hash_unwlock();

    return p_st_ct;
}

/*******************************************************************************
*  FUNC     :  refcnt减1
*  ARGS     :  *p_st_ct (Conntrack)
*  RTN      :
*  NOTE     : conntrack里的refcnt减1
*******************************************************************************/
void frct_put(Conntrack *p_st_ct)
{
    if(p_st_ct==NULL)
        return;

    __sync_fetch_and_sub(&p_st_ct->refcnt,1);

    return;
}

/*******************************************************************************
*  FUNC     :  ct的stauts转化为字符串
*  ARGS     :  ui_flg_status       (uint16_t)
            :
*  RTN      :
*  NOTE     :  不需要释放内存
*******************************************************************************/
const char *frct_status(uint16_t ui_flg_status)
{
    switch(ui_flg_status)
    {
        case APM_CONNECT_SYN_SENT:
            return "SYN_SENT";
            break;
        case APM_CONNECT_SYN_RECV:
            return "SYN_RECV";
            break;
        case APM_CONNECT_ESTABLISHED:
            return "ESTABLISHED";
            break;
        case APM_CONNECT_CLOSED:
            return "CLOSED";
            break;
        case APM_CONNECT_TIMEOUT:
            return "TIMEOUT";
            break;
        default:
            return "UNKNOWN";
            break;
    }

    return "UNKNOWN";
}

/*******************************************************************************
*  FUNC     :  l7的stauts转化为字符串
*  ARGS     :  ui_flg_status       (uint16_t)
            :
*  RTN      :
*  NOTE     :  不需要释放内存
*******************************************************************************/
const char *frct_l7status(uint16_t ui_flg_status)
{
    switch(ui_flg_status)
    {
        case APM_L7PROTO_STATUS_IN_PROGRESS:
            return "L7PROTO_IN_PROGRESS";
            break;
        case APM_L7PROTO_STATUS_FAIL:
            return "L7PROTO_FAIL";
            break;
        case APM_L7PROTO_STATUS_DONE:
            return "L7PROTO_DONE";
            break;
        default:
            return "L7PROTO_UNKNOWN";
            break;
    }

    return "L7PROTO_UNKNOWN";
}
